feat: search oraganization and view basic details#49
feat: search oraganization and view basic details#49kunal-bunkar wants to merge 1 commit intoAOSSIE-Org:mainfrom
Conversation
…oraganization using github's api
WalkthroughThis pull request introduces a GitHub Organization Explorer feature by setting up client-side routing, adding GitHub API integration, creating search and detail pages, and integrating Tailwind CSS for styling. New dependencies ( Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant LandingPage
participant GitHubAPI
participant Router
participant OrgDetailPage
User->>LandingPage: Enter search query & submit
LandingPage->>LandingPage: Validate input
LandingPage->>GitHubAPI: searchOrganizations(query)
GitHubAPI-->>LandingPage: Results array
alt Single Result Found
LandingPage->>Router: Navigate to /org/{login}
Router->>OrgDetailPage: Load page
OrgDetailPage->>GitHubAPI: fetchOrganization(login)
GitHubAPI-->>OrgDetailPage: Organization details
OrgDetailPage-->>User: Display org profile
else Multiple Results
LandingPage-->>User: Display results list
User->>LandingPage: Select organization
LandingPage->>Router: Navigate to /org/{login}
Router->>OrgDetailPage: Load page
OrgDetailPage->>GitHubAPI: fetchOrganization(login)
GitHubAPI-->>OrgDetailPage: Organization details
OrgDetailPage-->>User: Display org profile
else No Results
LandingPage->>GitHubAPI: fetchOrganization(query)
GitHubAPI-->>OrgDetailPage: Organization details
OrgDetailPage-->>User: Display org profile
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/LandingPage.tsx`:
- Around line 58-64: The component LandingPage currently has hardcoded
user-facing strings (e.g., "OrgExplorer", the paragraph "Search public GitHub
organizations by name or handle. Open a profile for a quick summary.", and other
strings like "Pick an organization" and "Organization on GitHub"); replace these
with i18n resource keys by adding entries to your locale/resource file and using
your project's translation helper in LandingPage (e.g., import and call the
translation function such as useTranslation/t or similar) to render
t('landing.title'), t('landing.description'), t('landing.pickOrganization'),
t('landing.organizationOnGithub'), etc.; ensure you update all occurrences
referenced in the comment (including strings around lines 58–64, 109–110,
128–133), add the new keys to the resource file(s), and run/localize tests to
confirm rendering works.
- Around line 39-40: The fallback navigation uses the raw trimmed query q which
can break routes for spaces/special chars; update the code that calls
navigate(`/org/${q}`) (after await fetchOrganization(q)) to URL-encode the query
using encodeURIComponent(q) so the path is safe (e.g.,
navigate(`/org/${encodeURIComponent(q)}`)); keep using the same q and
fetchOrganization call but ensure the encoded value is used only for the
navigation.
- Around line 16-48: handleSubmit can start overlapping requests which allow
stale responses to overwrite newer results; create and use an AbortController
per submit to cancel any previous in-flight request before starting a new one,
pass controller.signal into searchOrganizations (update src/api/github.ts to
accept an optional AbortSignal and forward it to fetch) and into
fetchOrganization if it is used for the fallback, store the current controller
(e.g., in a ref) and call abort() before creating a new controller, and ensure
you clean up the controller in the finally block so setResults/setError come
from the latest non-aborted response.
In `@src/pages/OrgDetailPage.tsx`:
- Around line 97-106: In OrgDetailPage, externalize all user-visible literal
strings (e.g., the "@{org.login}" prefix, labels like "Location", "Website", "X
/ Twitter", "Public repositories", "Followers", "Created", "View on GitHub", and
messages like "Missing organization.") into your i18n resource files and replace
the inline literals in the JSX with lookups (e.g., via your project's
translation hook/utility). Update the OrgDetailPage component where h1, the
username p, description p, and the label/value blocks render text to use
translation keys (create descriptive keys such as org.title, org.handlePrefix,
org.locationLabel, org.websiteLabel, org.twitterLabel, org.publicReposLabel,
org.followersLabel, org.createdLabel, org.viewOnGithub, org.missingOrg) and wire
them to the resource files for all languages.
- Around line 36-61: Replace the cancelled-flag pattern with an AbortController
to actually cancel the network request: create an AbortController inside the
useEffect, pass its signal to fetchOrganization (update fetchOrganization to
accept an optional signal parameter), and on cleanup call controller.abort();
keep the existing guards that avoid state updates (setOrg, setError, setLoading)
if the component unmounted or the request was aborted. Ensure fetchOrganization
forwards the signal to fetch/axios and throws or rejects appropriately so the
catch block handles aborts without updating state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 45342133-cc1a-4465-b8b4-5f7c4a816901
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (8)
package.jsonsrc/App.csssrc/App.tsxsrc/api/github.tssrc/index.csssrc/pages/LandingPage.tsxsrc/pages/OrgDetailPage.tsxvite.config.ts
💤 Files with no reviewable changes (1)
- src/App.css
| async function handleSubmit(e: FormEvent) { | ||
| e.preventDefault(); | ||
| const q = query.trim(); | ||
| if (!q) { | ||
| setError("Enter an organization name or login."); | ||
| setResults(null); | ||
| return; | ||
| } | ||
|
|
||
| setLoading(true); | ||
| setError(null); | ||
| setResults(null); | ||
|
|
||
| try { | ||
| const data = await searchOrganizations(q); | ||
| if (data.items.length > 0) { | ||
| setResults(data.items); | ||
| if (data.items.length === 1) { | ||
| navigate(`/org/${data.items[0].login}`); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| await fetchOrganization(q); | ||
| navigate(`/org/${q}`); | ||
| } catch (err) { | ||
| const message = | ||
| err instanceof Error ? err.message : "Something went wrong."; | ||
| setError(message); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| } |
There was a problem hiding this comment.
Missing request cancellation for overlapping searches.
The linked issue #47 explicitly requires "cancel overlapping requests so results remain current." Currently, if a user submits multiple searches quickly, stale responses could overwrite fresher results. Consider using AbortController to cancel in-flight requests when a new search begins.
🛠️ Suggested approach
+import { useRef } from "react";
+// ...
+
export function LandingPage() {
const navigate = useNavigate();
+ const abortRef = useRef<AbortController | null>(null);
const [query, setQuery] = useState("");
// ...
async function handleSubmit(e: FormEvent) {
e.preventDefault();
const q = query.trim();
if (!q) {
setError("Enter an organization name or login.");
setResults(null);
return;
}
+ // Cancel any in-flight request
+ abortRef.current?.abort();
+ abortRef.current = new AbortController();
+
setLoading(true);
setError(null);
setResults(null);
try {
- const data = await searchOrganizations(q);
+ const data = await searchOrganizations(q, abortRef.current.signal);
// ...This requires updating searchOrganizations in src/api/github.ts to accept an optional AbortSignal and pass it to fetch.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/LandingPage.tsx` around lines 16 - 48, handleSubmit can start
overlapping requests which allow stale responses to overwrite newer results;
create and use an AbortController per submit to cancel any previous in-flight
request before starting a new one, pass controller.signal into
searchOrganizations (update src/api/github.ts to accept an optional AbortSignal
and forward it to fetch) and into fetchOrganization if it is used for the
fallback, store the current controller (e.g., in a ref) and call abort() before
creating a new controller, and ensure you clean up the controller in the finally
block so setResults/setError come from the latest non-aborted response.
| await fetchOrganization(q); | ||
| navigate(`/org/${q}`); |
There was a problem hiding this comment.
URL-encode the fallback navigation path.
When no search results are found, the raw trimmed query q is used directly in the navigation path. If the query contains special characters or spaces, this could cause routing issues.
🛡️ Proposed fix
await fetchOrganization(q);
- navigate(`/org/${q}`);
+ navigate(`/org/${encodeURIComponent(q)}`);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await fetchOrganization(q); | |
| navigate(`/org/${q}`); | |
| await fetchOrganization(q); | |
| navigate(`/org/${encodeURIComponent(q)}`); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/LandingPage.tsx` around lines 39 - 40, The fallback navigation uses
the raw trimmed query q which can break routes for spaces/special chars; update
the code that calls navigate(`/org/${q}`) (after await fetchOrganization(q)) to
URL-encode the query using encodeURIComponent(q) so the path is safe (e.g.,
navigate(`/org/${encodeURIComponent(q)}`)); keep using the same q and
fetchOrganization call but ensure the encoded value is used only for the
navigation.
| <h1 className="mb-4 text-4xl font-bold tracking-tight text-white sm:text-5xl"> | ||
| OrgExplorer | ||
| </h1> | ||
| <p className="mx-auto max-w-md text-pretty text-sm leading-relaxed text-zinc-400 sm:text-base"> | ||
| Search public GitHub organizations by name or handle. Open a profile | ||
| for a quick summary. | ||
| </p> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
User-visible strings are hardcoded.
Per coding guidelines, user-visible strings should be externalized to resource files for internationalization (i18n). Strings like "OrgExplorer", "Search public GitHub organizations...", "Pick an organization", "Organization on GitHub", etc. are currently hardcoded throughout the component.
Also applies to: 109-110, 128-133
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/LandingPage.tsx` around lines 58 - 64, The component LandingPage
currently has hardcoded user-facing strings (e.g., "OrgExplorer", the paragraph
"Search public GitHub organizations by name or handle. Open a profile for a
quick summary.", and other strings like "Pick an organization" and "Organization
on GitHub"); replace these with i18n resource keys by adding entries to your
locale/resource file and using your project's translation helper in LandingPage
(e.g., import and call the translation function such as useTranslation/t or
similar) to render t('landing.title'), t('landing.description'),
t('landing.pickOrganization'), t('landing.organizationOnGithub'), etc.; ensure
you update all occurrences referenced in the comment (including strings around
lines 58–64, 109–110, 128–133), add the new keys to the resource file(s), and
run/localize tests to confirm rendering works.
| useEffect(() => { | ||
| let cancelled = false; | ||
|
|
||
| fetchOrganization(login) | ||
| .then((data) => { | ||
| if (!cancelled) { | ||
| setOrg(data); | ||
| setError(null); | ||
| } | ||
| }) | ||
| .catch((err) => { | ||
| if (!cancelled) { | ||
| setError( | ||
| err instanceof Error ? err.message : "Failed to load organization.", | ||
| ); | ||
| setOrg(null); | ||
| } | ||
| }) | ||
| .finally(() => { | ||
| if (!cancelled) setLoading(false); | ||
| }); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [login]); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Good cancellation pattern, but consider using AbortController for efficiency.
The cancelled flag correctly prevents state updates after unmount or when login changes. However, the fetch request itself continues to completion. For improved efficiency (especially on slow networks), you could use AbortController to actually cancel the HTTP request.
♻️ Optional enhancement using AbortController
useEffect(() => {
- let cancelled = false;
+ const controller = new AbortController();
- fetchOrganization(login)
+ fetchOrganization(login, controller.signal)
.then((data) => {
- if (!cancelled) {
- setOrg(data);
- setError(null);
- }
+ setOrg(data);
+ setError(null);
})
.catch((err) => {
- if (!cancelled) {
- setError(
- err instanceof Error ? err.message : "Failed to load organization.",
- );
- setOrg(null);
- }
+ if (err.name === "AbortError") return;
+ setError(
+ err instanceof Error ? err.message : "Failed to load organization.",
+ );
+ setOrg(null);
})
.finally(() => {
- if (!cancelled) setLoading(false);
+ setLoading(false);
});
return () => {
- cancelled = true;
+ controller.abort();
};
}, [login]);This requires updating fetchOrganization in src/api/github.ts to accept an optional signal parameter.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/OrgDetailPage.tsx` around lines 36 - 61, Replace the cancelled-flag
pattern with an AbortController to actually cancel the network request: create
an AbortController inside the useEffect, pass its signal to fetchOrganization
(update fetchOrganization to accept an optional signal parameter), and on
cleanup call controller.abort(); keep the existing guards that avoid state
updates (setOrg, setError, setLoading) if the component unmounted or the request
was aborted. Ensure fetchOrganization forwards the signal to fetch/axios and
throws or rejects appropriately so the catch block handles aborts without
updating state.
| <h1 className="text-2xl font-bold tracking-tight text-white sm:text-3xl"> | ||
| {org.name || org.login} | ||
| </h1> | ||
| <p className="mt-1 font-medium text-emerald-400/90"> | ||
| @{org.login} | ||
| </p> | ||
| {org.description && ( | ||
| <p className="mt-4 text-sm leading-relaxed text-zinc-400"> | ||
| {org.description} | ||
| </p> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
User-visible strings should be externalized for i18n.
Consistent with the feedback on LandingPage.tsx, labels such as "Location", "Website", "X / Twitter", "Public repositories", "Followers", "Created", "View on GitHub", "Missing organization.", etc. should be externalized to resource files for internationalization support.
Also applies to: 115-116, 121-134, 140-151, 153-162
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/OrgDetailPage.tsx` around lines 97 - 106, In OrgDetailPage,
externalize all user-visible literal strings (e.g., the "@{org.login}" prefix,
labels like "Location", "Website", "X / Twitter", "Public repositories",
"Followers", "Created", "View on GitHub", and messages like "Missing
organization.") into your i18n resource files and replace the inline literals in
the JSX with lookups (e.g., via your project's translation hook/utility). Update
the OrgDetailPage component where h1, the username p, description p, and the
label/value blocks render text to use translation keys (create descriptive keys
such as org.title, org.handlePrefix, org.locationLabel, org.websiteLabel,
org.twitterLabel, org.publicReposLabel, org.followersLabel, org.createdLabel,
org.viewOnGithub, org.missingOrg) and wire them to the resource files for all
languages.
Addressed Issues:
Fixes #48
Screenshots/Recordings:
Landing page


Dashboard page
Additional Notes:
-Routes: / (landing + search), /org/:login (detail); unknown paths redirect to /
-API: GitHub REST API from the browser only (fetch to api.github.com) — no backend. Search uses search/users with type:org; if there are no results, the app tries GET /orgs/:login for an exact handle.
-How to verify: npm install → npm run dev → search e.g. aossie or vercel → confirm detail loads and Back to search returns home.
-Rate limits: Unauthenticated GitHub API limits apply (documented in UI hint on landing).
Checklist
Summary by CodeRabbit
Release Notes
New Features
Styling